package com.zeny.express.framework.log.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.EvaluationContext;

import com.zeny.express.framework.log.annotation.LogBusiness;
import com.zeny.express.framework.log.entity.BusinessLog;
import com.zeny.express.framework.log.entity.BusinessLogDetailEntity;
import com.zeny.express.framework.shared.util.UUIDUtils;

public abstract class BusinessAspectSupport {

	protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
	
	private final BusinessExpressionEvaluator evaluator = new BusinessExpressionEvaluator();

	protected BusinessLog constructBusinessLog(BusinessLog businessLog, LogBusiness logBusiness, Object entityId, Object entityCode, Object entityName) {

		String businessId = logBusiness.businessId();
		String businessCode = logBusiness.businessCode();
		Date now = new Date();
		
		if (StringUtils.isNotBlank(businessId)) {
			businessLog.setId(businessId);
		} else {
			businessLog.setId(UUIDUtils.getTimeUUID(now));
		}
		businessLog.setBusinessCode(businessCode);
		businessLog.setBusinessTime(now);
		// TODO
		businessLog.setActionId(null);
		businessLog.setAppCode(null);
		businessLog.setUserCode(null);
		businessLog.setUserName(null);
		businessLog.setOrgCode(null);
		businessLog.setOrgName(null);

		List<BusinessLogDetailEntity> details = new ArrayList<BusinessLogDetailEntity>();

		if ((entityId != null && entityId instanceof Collection<?>)
				|| (entityCode != null && entityCode instanceof Collection<?>)
				|| (entityName != null && entityName instanceof Collection<?>)) { // is collection

			if (entityId != null && entityId instanceof Collection<?>) {
				Object[] array = (Object[]) ((Collection<?>) entityId)
						.toArray();

				for (int i = 0; i < array.length; ++i) {
					BusinessLogDetailEntity detail = createBusinessLogDetailEntity(businessLog);
					detail.setEntityId((String)array[i]);
					details.add(detail);
				}
			}
			if (entityCode != null && entityCode instanceof Collection) {
				Object[] array = (Object[]) ((Collection<?>) entityCode)
						.toArray();

				for (int i = 0; i < array.length; ++i) {
					BusinessLogDetailEntity detail = null;
					if (details.size() > i) {
						detail = details.get(i);
						detail.setEntityCode((String)array[i]);
					} else {
						detail = createBusinessLogDetailEntity(businessLog);
						detail.setEntityCode((String)array[i]);
						details.add(detail);
					}
				}
			}
			if (entityName != null && entityName instanceof Collection) {
				Object[] array = (Object[]) ((Collection<?>) entityName)
						.toArray();

				for (int i = 0; i < array.length; ++i) {
					BusinessLogDetailEntity detail = null;
					if (details.size() > i) {
						detail = details.get(i);
						detail.setEntityName((String)array[i]);
					} else {
						detail = createBusinessLogDetailEntity(businessLog);;
						detail.setEntityName((String)array[i]);
						details.add(detail);
					}
				}
			}

		} else {	// is single
			BusinessLogDetailEntity detail = createBusinessLogDetailEntity(businessLog);;
			if (entityId != null && entityId instanceof String) {
				detail.setEntityId((String) entityId);
			}
			if (entityCode != null && entityCode instanceof String) {
				detail.setEntityCode((String) entityCode);
			}
			if (entityName != null && entityName instanceof String) {
				detail.setEntityName((String) entityName);
			}
			
			if (StringUtils.isNotBlank(detail.getEntityId())
				|| StringUtils.isNotBlank(detail.getEntityCode())
				|| StringUtils.isNotBlank(detail.getEntityName())) {
				details.add(detail);
			}
		}
		
		businessLog.setDetails(details);
		businessLog.setDetailSize(details.size());
		
		return businessLog;
	}
	
	protected BusinessLogDetailEntity createBusinessLogDetailEntity(BusinessLog businessLog) {
		BusinessLogDetailEntity detail = new BusinessLogDetailEntity();
		detail.setId(UUIDUtils.getTimeUUID(businessLog.getBusinessTime()));
		detail.setBusinessId(businessLog.getId());
		detail.setBusinessCode(businessLog.getBusinessCode());
		detail.setBusinessTime(businessLog.getBusinessTime());
		
		return detail;
	}

	protected class OperationContext {
		
		private final BusinessOperation operation;

		private final Method method;

		private final Object[] args;

		private final Object target;

		private final Class<?> targetClass;

		public OperationContext(BusinessOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
			this.operation = operation;
			this.method = method;
			this.args = args;
			this.target = target;
			this.targetClass = targetClass;
		}

		protected Object generateEntityId(Object result) {
			if (StringUtils.isBlank(this.operation.getEntityId())) {
				return StringUtils.EMPTY;
			}
			
			EvaluationContext evaluationContext = createEvaluationContext(result);
			return evaluator.id(this.operation.getEntityId(), this.method, evaluationContext);
		}

		protected Object generateEntityName(Object result) {
			if (StringUtils.isBlank(this.operation.getEntityName())) {
				return StringUtils.EMPTY;
			}
			
			EvaluationContext evaluationContext = createEvaluationContext(result);
			return evaluator.name(this.operation.getEntityName(), this.method, evaluationContext);
		}

		protected Object generateEntityCode(Object result) {
			if (StringUtils.isBlank(this.operation.getEntityCode())) {
				return StringUtils.EMPTY;
			}
			
			EvaluationContext evaluationContext = createEvaluationContext(result);
			return evaluator.code(this.operation.getEntityCode(), this.method, evaluationContext);
		}

		private EvaluationContext createEvaluationContext(Object result) {
			return evaluator.createEvaluationContext(this.method, this.args, this.target, this.targetClass, result);
		}
	}

}
